﻿/*
 * 陈洋洋，2011-06-07
 * 机器人控制器服务器端
 * 通过蓝牙连接机器人，提供远程socket连接，监听socket端口5558
 * 采集yuv420sp图片帧压缩成jpeg，监听socket5556端口
 * 可以以菜单的方式提供蓝牙连接和蓝牙通信内容的调试界面，用于调试或者其他
 * 
 * 陈洋洋。2011-07-03
 * 改进好了服务器，增加密码验证，视频帧传输更加流畅，服务器更加稳定
 * 
 * 陈洋洋，2011-07-05
 * 修改好了网页布局混乱
 * 
 * 陈洋洋，2011-07-10
 * 修改了源代码 的编码为UTF-8，避免了中文注释成为乱码
 */
package com.robotcontroller.buaa;

import java.util.concurrent.Semaphore;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.List;



import android.app.Activity;
import android.app.AlertDialog;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.content.DialogInterface;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.graphics.YuvImage;
import android.hardware.Camera;
import android.hardware.Camera.Size;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.Window;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;

public class MainActivity extends Activity 
{
	//调试
    private static final String TAG = "Robot";
    private static final boolean D = true;
    int debugsum = 0;
    
	//获取手机屏幕分辨率的类  
    private DisplayMetrics dm ;
    private int heightPix;
    private int widthPix;

  //private static Handler mhandlerll;            //用于向自定义线程发送消息的Handler
    
   // private static boolean isforeground = false;  //是否将进入后台。

    //socket5558 的连接状态
    private static final int NET_STATE_NONE = 0;       //网络未连接
    private static final int NET_STATE_LISTENNING = 1; //正在监听
    private static final int NET_STATE_CONNECTED = 2;  //已经连接
    private static int NET_STATE = NET_STATE_NONE ;     //当前网络状态
    
    //网络socket通信的消息类型
    public final static int MSG_MANULCONTROL = 8;
    public final static int MSG_AUTOCTL_TRACE = 9;
    
    private static boolean isCtrObjectTra = false;  //是否跟踪物体
    // BluetoothService Handler发送的消息类型
    public static final int BTMSG_STATE_CHANGE  = 1;
    public static final int BTMSG_READ = 2;
    public static final int BTMSG_WRITE = 3;
    public static final int BTMSG_DEVICE_NAME = 4;
    public static final int BTMSG_TOAST = 5;
    
    //MyHttpServer Handler发送的消息类型
    public static final int MHSMSG_TOROBOT = 20;  //来自MyHttpServer，解析命令，发送给给机器人蓝牙
    
    // Key names received from the BluetoothService Handler
    public static final String DEVICE_NAME = "device_name";
    public static final String TOAST = "toast";
    
    //蓝牙状态,蓝牙可用，蓝牙激活；
    private boolean isBtAvailable = false;
    private boolean isBtenabled = false;
    static int CMDMAXLENTOROBOT = 50; //定义向机器人发送的蓝牙命令长度，不超过50，具体根据协议
    
    //互斥访问yuv数组信号量
    private static Semaphore yuvbufferlock = new Semaphore(1);
    //互斥访问jpeg数组信号量
    public static Semaphore jpegbufferlock = new Semaphore(1);
        
    private static int yuvimglen = 0;    //camera生成的YUV数据长度
    private static int previewwidth = 320;      //yuv图像的设置的宽度
    private static int previewheight = 240;     //yuv图像的设置的高度
    
    
    //yuv数据数组
    static byte[] yuvtempbuf = new byte[previewheight*previewwidth*3/2]; //640*480*1.5 = 460800B,即是最大的preview的YUV420SP数据的大小
    static byte[] decodeyuvbuf = new byte[previewheight*previewwidth*3/2];
    static int[] rgbabuffer = new int[previewheight*previewwidth]; 
    
    //private static ObjectDetect mydetector = new ObjectDetect();
    private static boolean isfirstload = true;
    private static boolean loadok = false;
    
   // static byte[] rotatebuffer = new byte[115200];
  //  private HttpListenThread sendthread = null;     //在onPreviewFrame()中开启
    private BtFwServerThread socketlisbtsend = null;
    private BtFwClientThread clientthread = null;
    
    //生成jpeg函数类
    public static boolean NOSTOPED= true;  //是否停发向客户端发送jpeg流的标志
	public static YuvImage yuvImage = null;
	public static Rect rectangle = null;			   	    
    public static int frame = 0;
    public int sendlength = 0;
    public int times = 0;
    
    // Intent request codes
    private static final int REQUEST_CONNECT_DEVICE = 1;
    private static final int REQUEST_ENABLE_BT = 2;

    // Layout Views
    private TextView mTitle;
    //显示预览和处理预览的控件，相机设备对象
	private SurfaceView mSurfaceView;
	private SurfaceHolder mSfHolder;
	private Camera mCamera = null;
	Bitmap mBitmap = null;
    
	//预览回调
	private Camera.PreviewCallback  previewCallback=null; 
    
    // Name of the connected device
    private String mConnectedDeviceName = null;
    // Array adapter for the conversation thread
    private ArrayAdapter<String> mConversationArrayAdapter;
    // String buffer for outgoing messages
    private static StringBuffer mOutStringBuffer;
    // Local Bluetooth adapter
    private BluetoothAdapter mBluetoothAdapter = null;
    // Member object for the chat services
    private static BluetoothService mChatService = null;

    
    //机器人控制命令对象
    private static RobotControlCommand robotcontroller =  new RobotControlCommand();
    
    /*
    public native static void FindFeatures(int width, int height, byte yuv[], int[] rgba , int[] argv);
    static {
        System.loadLibrary("cyy_opencv_detect");
    }
    */
    
    /*
    protected Bitmap processFrame(byte[] data) {
    	int width = 320 , height = 240;
        int frameSize = width * height;
       // int[] rgba = new int[frameSize];
       // int[] argv = new int[2];
        
        FindFeatures(width , height , data, null , null);

        Bitmap bmp = Bitmap.createBitmap(width , height , Bitmap.Config.ARGB_8888);
        //bmp.setPixels(rgba,, width , 0, 0, width, height);
        // offset  , stride
        return bmp;
    }
    */
    
    //当Acitivity第一次被创建的时候调用
    @Override
    public void onCreate(Bundle savedInstanceState) 
    {
        super.onCreate(savedInstanceState);
        Log.i("cyy","onCreat....");

        // Set up the window layout
        requestWindowFeature(Window.FEATURE_CUSTOM_TITLE);
        
        dm = new DisplayMetrics();  
        getWindowManager().getDefaultDisplay().getMetrics(dm);  
        heightPix = dm.heightPixels;
        widthPix = dm.widthPixels ;
        Log.i("cyy" , "screen definition,w:" + widthPix +" h:"+ heightPix  );
        //480*800，加载默认布局
        //320*480，加载布局320*480
        if(480==heightPix&&320==widthPix)
        {
        	setContentView(R.layout.main_480_320);
        }
        else
        {
        	 setContentView(R.layout.main_800_480);
        }
        
        //setContentView(R.layout.main);
        getWindow().setFeatureInt(Window.FEATURE_CUSTOM_TITLE, R.layout.custom_title);    
               
         // Set up the custom title
         mTitle = (TextView) findViewById(R.id.title_left_text);
         mTitle.setText(R.string.app_name);
         mTitle = (TextView) findViewById(R.id.title_right_text);
           
        // Get local Bluetooth adapter
        mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();

        // If the adapter is null, then Bluetooth is not supported
        if (mBluetoothAdapter == null) {
            Toast.makeText(this, "Bluetooth is not available", Toast.LENGTH_LONG).show();
            finish();
            return;
        }
        isBtAvailable = true;
        
    	previewCallback = new Camera.PreviewCallback() {
			@Override
			public synchronized void onPreviewFrame(byte[] data, Camera camera) {
				//将data[]中的数据发送出去，data[]的数据是线程安全的，有同步机制
			    try {
			    	yuvimglen = data.length;
			    	
				    	//规定，各个线程互斥得访问sendbuffer
				    	yuvbufferlock.acquire();
				    	System.arraycopy(data , 0 , yuvtempbuf , 0 , data.length);	
				    	yuvbufferlock.release();
				    	
				    	//sendthread.start();		
	
			       } catch (Exception ex) {
			       Log.v("System.out", ex.toString());
			       }//endtry

			}//endonPriview
		};
 	
		
        //设置预览控件
        mSurfaceView = (SurfaceView)this.findViewById(R.id.SurfaceView01); 
        mSfHolder = mSurfaceView.getHolder();
        
        mSfHolder.addCallback(new SurfaceHolder.Callback() {
        	
			@Override
			public void surfaceDestroyed(SurfaceHolder holder)
			{
				Log.i("camer","SurfaceHolder.Callback....surfaceDestroyed");
				//Toast.makeText(getApplicationContext(), "surfaceDestroyed", Toast.LENGTH_LONG).show();
				mCamera.stopPreview();
				mCamera.release();
				mCamera = null;
			}
			
			@Override
			public void surfaceCreated(SurfaceHolder holder)
			{
				//当预览视图创建的时候开启相机
				mCamera = Camera.open();
				try {
					Log.i("camer","SurfaceHolder.Callback....surfaceCreated");
					//mCamera.setDisplayOrientation(90);
					//设置预览
					mCamera.setPreviewDisplay(holder);					

				} catch (IOException e) {
					// 释放相机资源并置空
					mCamera.release();
					mCamera = null;
				}
			}
			
			//当surface视图数据发生变化时，处理预览信息
			@Override
			public void surfaceChanged(SurfaceHolder holder, int format, int width, int height)
			{
				//相机的参数是否设置有效，可以查看在DDMS中查看Log.i得知！！！
				Log.i("camer","SurfaceHolder.Callback....surfaceChanged");
			//	Toast.makeText(getApplicationContext(), "surfaceChanged", Toast.LENGTH_LONG).show();
				Camera.Parameters parameters = mCamera.getParameters();
				parameters.setPictureFormat(PixelFormat.JPEG);
				
				//获取caera支持的PictrueSize，看看能否设置？？
				List<Size> pictureSizes = mCamera.getParameters().getSupportedPictureSizes();
				List<Size> previewSizes = mCamera.getParameters().getSupportedPreviewSizes();
				List<Integer> previewFormats = mCamera.getParameters().getSupportedPreviewFormats();
				mCamera.getParameters().getSupportedPreviewFrameRates();
				Log.i("camer","cyy support parameters is ");
					
				Size psize = null;
				for(int i=0 ; i<pictureSizes.size() ; i++)
				{
				   psize = pictureSizes.get(i);
				   Log.i("camer","cyy PictrueSize,width: "+psize.width+" height"+psize.height);
				}
				for(int i=0 ; i<previewSizes.size() ; i++)
				{
				   psize = previewSizes.get(i);
				   Log.i("camer","cyy PreviewSize,width: "+psize.width+" height"+psize.height);
				}
				Integer pf = null;
				for(int i=0 ; i<previewFormats.size() ; i++)
				{
					pf = previewFormats.get(i);
					Log.i("camer","cyy previewformates:"+pf);
				}
				
				
				parameters.setPictureSize(640, 480);

     			parameters.setPreviewSize(previewwidth , previewheight);  //设置成功
				//parameters.setPreviewSize(176 , 144); 

				//parameters.setPreviewFrameRate(15);    //没有修改成功。
	            parameters.setPreviewFormat(PixelFormat.YCbCr_420_SP);  
     			//parameters.setPreviewFormat(ImageFormat.YUY2 );  
     			
	            //该函数只有Camera.PictureCallback才生效
	     	    //parameters.setRotation(90);////在portrait模式下，预览方向始终不变化?
	            
	            mCamera.setDisplayOrientation(90);  //预览方向
				mCamera.setPreviewCallback(previewCallback);
				mCamera.setParameters(parameters);	
				mCamera.startPreview();
				
				Camera.Size csize = mCamera.getParameters().getPreviewSize ();
				previewheight = csize.height;
				previewwidth = csize.width;
				
				Log.i("cyy", "cyy ,after setting, previewSize:width: "+csize.width+" height: "+csize.height);
				csize = mCamera.getParameters().getPictureSize();
				Log.i("cyy", "cyy ,after setting, pictruesize:width: "+csize.width+" height: "+csize.height);
			    Log.i("cyy", "cyy ,after setting, previewformate is "+mCamera.getParameters().getPreviewFormat());
			    Log.i("cyy", "cyy ,after setting, previewframeRate is "+mCamera.getParameters().getPreviewFrameRate());			    
			    //终于设置成功了！！！
			}
		
		});//endofaddcallback
         
        mSfHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);   
       
        //YUV420转Jpeg用到对象
       rectangle = new Rect(0 , 0 , previewwidth , previewheight);	
	   yuvImage = new YuvImage(decodeyuvbuf, PixelFormat.YCbCr_420_SP , previewwidth, previewheight, null);
       //yuvImage = new YuvImage(tempsendbuffer, ImageFormat.YUY2 , previewwidth, previewheight, null);
       
	   //4,17,20
	   //开启httpserver监听8080端口，接受视频帧请求
        new MyHttpServer(8080 , mHandler);
        
        //tcp协议，监听5558端口，接受远程请求，转发蓝牙命令
        socketlisbtsend = new BtFwServerThread(5558);
        socketlisbtsend.start();
        
    
    }//endOncreat
    
    
    @Override
    public void onStart() {
        super.onStart();
        if(D) Log.e(TAG, "++ ON START ++");

        // If BT is not on, request that it be enabled.
        // setupChat() will then be called during onActivityResult
        if (!mBluetoothAdapter.isEnabled()) {
            Intent enableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
            startActivityForResult(enableIntent, REQUEST_ENABLE_BT);
        // Otherwise, setup the chat session
        } else {
            if (mChatService == null) SetupBt();
        }
    }

    @Override
    public synchronized void onResume() {
        super.onResume();
        if(D) Log.e(TAG, "+ ON RESUME +");

        //isforeground = false;
        
        // Performing this check in onResume() covers the case in which BT was
        // not enabled during onStart(), so we were paused to enable it...
        // onResume() will be called when ACTION_REQUEST_ENABLE activity returns.
        if (mChatService != null) {
            // Only if the state is STATE_NONE, do we know that we haven't started already
            if (mChatService.getState() == BluetoothService.STATE_NONE) {
              // Start the Bluetooth chat services
              mChatService.start();
            }
        }
    }
    
 
/*
 * 为蓝牙串口通信做些准备性的工作
 * 设置显示通信内容的控件
 * 创建自定义的 BluetoothChatService对象
 */
    private void SetupBt() {
        Log.d(TAG, "SetupBt()");

        //显示蓝牙通信内容的控件
         mConversationArrayAdapter = new ArrayAdapter<String>(this, R.layout.message);
        
        // Initialize the BluetoothService to perform bluetooth connections
        mChatService = new BluetoothService(this, mHandler);

        // Initialize the buffer for outgoing messages
        mOutStringBuffer = new StringBuffer("");
    }
    

    @Override
    public synchronized void onPause() {
        super.onPause();
        if(D) Log.e(TAG, "- ON PAUSE -");
        
       // isforeground = true;
    }

    @Override
    public void onStop() {
        super.onStop();
        if(D) Log.e(TAG, "-- ON STOP --");
        
        Toast.makeText(this, "senddate number is "+debugsum, Toast.LENGTH_LONG).show();
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        // Stop the Bluetooth chat services
        if (mChatService != null) mChatService.stop();
        if(D) Log.e(TAG, "--- ON DESTROY ---");
    }

    private void ensureDiscoverable() {
        if(D) Log.d(TAG, "ensure discoverable");
        if (mBluetoothAdapter.getScanMode() !=
            BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE) {
            Intent discoverableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
            discoverableIntent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 300);
            startActivity(discoverableIntent);
        }
    }

    /**
     * 向蓝牙远程端发送信息
     * 只有等蓝牙连接成功后才能发送成功
     * 接受的蓝牙信息由 mHandler处理，
     * 可以将传给客户端，也可以服务器端处理
     * 被自定义线程调用，故不能在该函数内更新UI
     */
    private static void sendCmd(byte[] cmdbuf , int offset , int count) 
    {
        // Check that we're actually connected before trying anything
        if (mChatService.getState() != BluetoothService.STATE_CONNECTED) 
        {
          // Toast.makeText(this, R.string.not_connected, Toast.LENGTH_SHORT).show();
        	Log.i("cyy" , "buletooch is not connected");
            return;
        }

        // Check that there's actually something to send
        if (cmdbuf.length > 0 && count <= CMDMAXLENTOROBOT) 
        {
            // Get the message bytes and tell the BluetoothService to write
            byte[] send = cmdbuf;
            mChatService.write(send , offset , count);
            
            // Reset out string buffer to zero 
            mOutStringBuffer.setLength(0);
            return ;
        }
        //Toast.makeText(this, "Message sent to robot is too much", Toast.LENGTH_SHORT).show();
        Log.i("cyy" , "bluetooch msg is too long");
    }

/*
 * 异步信息的处理
 * 机器人端蓝牙返回的信息保留起来，传给socket
 */
    private final Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) 
        {
            switch (msg.what) 
            {
            case BTMSG_STATE_CHANGE :   //蓝牙连接状态发生变化
                if(D) Log.i(TAG, "BTMSG_STATE_CHANGE : " + msg.arg1);
                switch (msg.arg1) 
                {
	                case BluetoothService.STATE_CONNECTED:
	                    mTitle.setText(R.string.title_connected_to);
	                    mTitle.append(mConnectedDeviceName);
	                    mConversationArrayAdapter.clear();
	                    isBtenabled = true;
	                    break;
	                case BluetoothService.STATE_CONNECTING:
	                    mTitle.setText(R.string.title_connecting);
	                    break;
	                case BluetoothService.STATE_LISTEN:
	                case BluetoothService.STATE_NONE:
	                    mTitle.setText(R.string.title_not_connected);
	                    break;
                }
                break;
            case BTMSG_WRITE:         //向蓝牙远程端发送信息
                byte[] writeBuf = (byte[]) msg.obj;
                // construct a string from the buffer
                String writeMessage = new String(writeBuf);
                mConversationArrayAdapter.add("Me:  " + writeMessage);
                break;
            case BTMSG_READ:          //从蓝牙远程端收到到信息
                byte[] readBuf = (byte[]) msg.obj;
                // construct a string from the valid bytes in the buffer
                String readMessage = new String(readBuf, 0, msg.arg1);
                mConversationArrayAdapter.add(mConnectedDeviceName+":  " + readMessage);
                //注意这里，可能有读不全的问题，实际测试下
                
                /*
                 * 1、只有主动发送命令给机器人，机器人才发送信息给手机蓝牙端
                 * 2、所以可以直接提取，没有必要另外去读输入流
                 * 3、直接检查获取值就行
                 * 
                 *    直接去分析提取   readBuf[]即可
                 *    将readbuf[]的内容通过socket发送给客户端
                 *    readBuf[]  长度是 msg.arg1
                 */
                 Log.i("cyy" , "recive rbt cmd, send to client");
                 int cmdlen = msg.arg1;
                 
                 //暂时保留此接口，留给其他
                 if(NET_STATE_CONNECTED == NET_STATE && null!=clientthread) 
                 {
                	 clientthread.writeInt(cmdlen);
                	 clientthread.write(readBuf, 0 , cmdlen);
                	// Log.i("cyy" , "send cmd to client");
                 }
                 
                 //向HTTP客户端推送。
                 if(true == MyHttpServer.IsWebSocketConnected())
                 {
                	 MyHttpServer.pushToClient(readBuf, 0, cmdlen);
                 }
                	 
                break;
            case BTMSG_DEVICE_NAME:
                // save the connected device's name
                mConnectedDeviceName = msg.getData().getString(DEVICE_NAME);
                Toast.makeText(getApplicationContext(), "Connected to "
                               + mConnectedDeviceName, Toast.LENGTH_SHORT).show();
                break;
            case BTMSG_TOAST:
                Toast.makeText(getApplicationContext(), msg.getData().getString(TOAST),
                               Toast.LENGTH_SHORT).show();
                break;
                
            case MHSMSG_TOROBOT:  //来自MyHttpServer的消息，用于控制机器人
            {
            	/* 1、接受
            	 * 2、解析，
            	 * 3、解析正确则蓝牙发送
            	 */
            	byte[] buffer = (byte[])msg.obj;
            	int offset = msg.arg1;
            	int count = msg.arg2;
            	
            	//debug
            	Log.i("cyy" , "recive from websocketclient:");
            	for(int i= offset; i<(offset+count) ; i++)
            	{
                   	Log.i("cyy" , " "+ buffer[i]);
            	}
                
            	//解析命令buffer,offset,count，改命令由网页发送的，需要自己设置
            	
            	//转发给蓝牙
            	sendCmd(buffer, offset, count);
            	
            	break;
            }
            }
        }
    };

 /*
  * 处理发送的Intent意图返回的结果
  * 参数requestCode，Intent传递一个值
  * 参数resultCode，Intent触发的行为的返回结果
  * 参数data，Intent
  */
    public void onActivityResult(int requestCode, int resultCode, Intent data) {
        if(D) Log.d(TAG, "onActivityResult " + resultCode);
        switch (requestCode) {
        case REQUEST_CONNECT_DEVICE:
            // When DeviceListActivity returns with a device to connect
            if (resultCode == Activity.RESULT_OK) {
                // Get the device MAC address
                String address = data.getExtras()
                                     .getString(BluetoothScanActivity.EXTRA_DEVICE_ADDRESS);
                // Get the BLuetoothDevice object
                BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address);
                // Attempt to connect to the device
                mChatService.connect(device);
            }
            break;
        case REQUEST_ENABLE_BT:
            // When the request to enable Bluetooth returns
            if (resultCode == Activity.RESULT_OK) {
                // Bluetooth is now enabled, so set up a chat session
                SetupBt();
            } else {
                // User did not enable Bluetooth or an error occured
                Log.d(TAG, "BT not enabled");
                Toast.makeText(this, R.string.bt_not_enabled_leaving, Toast.LENGTH_SHORT).show();
                finish();
            }
        }
    }
    
    
	//按键处理函数！如果是多个Activity，是否会和默认的activity跳转冲突？
	public boolean onKeyDown(int keyCode, KeyEvent event)
	{
    	//按下键盘上返回按钮
    	if(keyCode == KeyEvent.KEYCODE_BACK)
    	{
    		ExitOptionsDialog();
    		return true;
    	}else
    	{		
    		return super.onKeyDown(keyCode, event);
    	}	
	}
    
  //退出软件的时候确认选择      	
    private void ExitOptionsDialog()
    {
    	  AlertDialog.Builder builder = new AlertDialog.Builder(this); 
    	  builder.setMessage("是否退出程序") 
    	         .setCancelable(false) 
    	         .setPositiveButton("确定", new DialogInterface.OnClickListener() { 
    	             public void onClick(DialogInterface dialog, int id) { 
    	            	 android.os.Process.killProcess(android.os.Process.myPid());
    	             } 
    	         }) 
    	         .setNegativeButton("取消", new DialogInterface.OnClickListener() { 
    	             public void onClick(DialogInterface dialog, int id) { 
    	                  dialog.cancel(); 
    	             } 
    	         }); 
    	  AlertDialog alert = builder.create();
    	  alert.show();
    }

    
//创建menu按键菜单
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        MenuInflater inflater = getMenuInflater();
        inflater.inflate(R.menu.option_menu, menu);
        return true;
    }
    
//menu按键菜单的选择函数
    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
        case R.id.scan:
            // Launch the DeviceListActivity to see devices and do scan
            Intent serverIntent = new Intent(this, BluetoothScanActivity.class);
            startActivityForResult(serverIntent, REQUEST_CONNECT_DEVICE);
            return true;
        case R.id.discoverable:
            // Ensure this device is discoverable by others
        	Toast.makeText(this, "senddate number is "+debugsum, Toast.LENGTH_LONG).show();
            ensureDiscoverable();
            return true;
        case R.id.rbcc:
             //显示蓝牙通信内容控件
        	if(isBtAvailable&&isBtenabled)
        	{
	        	//显示蓝牙通信内容以弹出对话框的形式
	        	ListView mListViewBtComu = new ListView(this);
	        	mListViewBtComu.setAdapter(mConversationArrayAdapter);
	        	mListViewBtComu.setSaveEnabled(true);
	        	mListViewBtComu.setScrollbarFadingEnabled(false);
	        	new AlertDialog.Builder(this).setTitle("蓝牙串口通信信息").setView(mListViewBtComu).setPositiveButton("确定", null).show();       	  	 
        	}
           return true;    
        }
        return false;
    }
    
    
    /*
     * 互斥调用该函数，发送jpeg图片线程互斥访问这个，返回Jpeg帧的长度
     * 参数为jpeg帧输出的数组流
     */
    public synchronized static int decodetojpeg(ByteArrayOutputStream arrayout) throws Exception
    {    
    	 int jpegframelen = 0;   
	  	 	  	
           //更新Yuv数据。	   
    	   yuvbufferlock.acquire();
    	   System.arraycopy(yuvtempbuf, 0, decodeyuvbuf , 0, yuvimglen);
    	   yuvbufferlock.release();
    	   
    	   if(isCtrObjectTra)   //来自客户端的标志，是否开启运动跟踪
    	   {
    		   traceobjectandgo(decodeyuvbuf , 80 , 80);
    	   }
    
    	         	   
    	   if( 0 != arrayout.size())     //每次循环,arrayout只保存当前最新的一帧图片！	
    	   {
    		   arrayout.reset();         
    	   }
    	      	  
    	   //将yuv数据编码成jpeg
        if(yuvImage.compressToJpeg(rectangle, 100, arrayout))
    	   {      
    		   frame++;	
    		   jpegframelen = arrayout.toByteArray().length;   		   
    	   }else
    	   {
    		   Log.i("cyy" , "cyy....failed to compress jpeg to arrayoutstream");
    		   return 0;
    	   }
    	   return jpegframelen;
       
    }

 /*
  * 从yuv420sp数组decodeyuvbuf中检测出特定物体，并且计算出该物体的水平中心和半径
  * 并发送命令给蓝牙控制机器人跟踪该物体
  */
    static byte respeed = 0x24;
    static boolean islastgoahead = false;
    private static void traceobjectandgo(byte[] yuvbuf , int minobjwidth , int minobjheight)
    {
	   	 int[] argv = new int[2];
	  	 int object_x = 0;
	  	 int object_width = 0;
	   //改进jni
	   if(isfirstload)
	   {  
		  loadok = ObjectDetect.load();
		  Log.i("cyy" , "load face xml file!");
	   }
	   
	   if(loadok)
	   {
		   //Log.i("cyy" , "load face xml ok!");
		   isfirstload = false;
		   ObjectDetect.dectect(previewwidth , previewheight , yuvbuf, minobjwidth , minobjheight ,  null , argv);
	   }
	   
	   object_x = argv[0];
	   object_width = argv[1];
	   
	   //previewheight ; previewwidth;
	   byte[] command = null;  //发送给机器人的命令
	   byte speed = 0x24;
 
	   
	   if(object_width !=0)  //检测出物体
	   {
		   if( (object_x < previewwidth/2) && ( (previewwidth/2 - object_x)>20) )
    	   {
    		   //物体在中轴线左边，左转。
    		   Log.i("cyy", "go left" );
    		   
				//按一次左转30度
				  byte id = 0x01;                //底盘的ID
				  byte[] cmdarg = {0x09 ,0x05};  //逆时针旋转0x05度， 
				                                 //09表示逆时针，0x14表示20读
				  command = robotcontroller.W_CMDtoRobot(id, cmdarg);
				  islastgoahead = false;
    	   }
    	   else if( (object_x < previewwidth/2) && ((previewwidth/2 - object_x)<=20))
    	   {
    		   //前进
    		   Log.i("cyy", "go ahead" );
				  byte id = 0x01;                //底盘的ID
				  byte[] cmdarg = {0x03 ,speed};  //沿Dir3方向36H速度前进		  
				  command = robotcontroller.W_CMDtoRobot(id, cmdarg);	
				  respeed = speed;
				  islastgoahead = true;
    	   }
    	   else if( (object_x > previewwidth/2) && ((object_x - previewwidth/2)<=20))
    	   {
    		   //前进
    		   Log.i("cyy", "go ahead" );
    			  byte id = 0x01;                //底盘的ID
				  byte[] cmdarg = {0x03 ,speed};  //沿Dir3方向36H速度前进		  
				  command = robotcontroller.W_CMDtoRobot(id, cmdarg);
				  respeed = speed;
				  islastgoahead = true;
    	   }
    	   else if( (object_x > previewwidth/2) && ((object_x - previewwidth/2) > 20))
    	   {
    		   //右转
    		   Log.i("cyy", "go right" );
				  byte id = 0x01;                //底盘的ID
				  byte[] cmdarg = {0x0A ,0x05};  //顺时 针旋转0x05度，即5度	 
				                                 //0x0A表示逆时针，0x05表示5度
				  command = robotcontroller.W_CMDtoRobot(id, cmdarg);
				  islastgoahead = false;
    	   }
		   
		   sendCmd(command , 0 , command.length );
		   
	   }else//未检测出物体
	   {  //沿着上一次的速度递减
		   if( islastgoahead )  //上一帧必须是检测到物体且前进状态下，这一帧检测不到，减速
		   {
			   respeed =(byte) (respeed - 0x07);
			   if(respeed <= 0) respeed = 0;
			   
				  byte id = 0x01;                //底盘的ID
				  byte[] cmdarg = {0x03 ,respeed};  //沿Dir3方向36H速度前进		  
				  command = robotcontroller.W_CMDtoRobot(id, cmdarg);
				  Log.i("cyy", "go ahead,speed:" +respeed );
		   }
		   else  //上次是左转或者右转，直接停止
		   {
    		   Log.i("cyy", "go ahead" );
				  byte id = 0x01;                //底盘的ID
				  byte[] cmdarg = {0x03 ,0x00};  //沿Dir3方向36H速度前进		  
				  command = robotcontroller.W_CMDtoRobot(id, cmdarg);	
		   }
		   
		   sendCmd(command , 0 , command.length );
	   }
	   
	 //  Log.i("cyy" , "objec x:"+object_x + ",ojbect width:" + object_width);
    }
    
   /*
    * socket监听，监听5558端口，接受tcp socket请求
    * 创建新的线程去处理客户的请求
    */
    class BtFwServerThread extends Thread
    {
    	private int mport;               //绑定的端口号
    	private ServerSocket mssocket;   //监听端口的socket
    	private Socket mcmsocket;        //与客户端通信的socket       
 
    	public BtFwServerThread(int nport)
    	{
    		mport = nport;
    	}
    	@Override
    	public void run()
    	{
    		try 
    		{
				mssocket = new ServerSocket(mport);
			} catch (IOException e) 
			{
				//端口绑定异常，改进，通知UI处理
				e.printStackTrace();
				return ;
			}
			
            //
			while(true)
			{
				try 
				{
					mcmsocket = mssocket.accept();
					//new BtFwClientThread(mcmsocket).start();
					clientthread = new BtFwClientThread(mcmsocket);
					clientthread.start();
					
				    Log.i("cyy" , "In BtFwServerThread,recive new client :"
				    		+mcmsocket.getRemoteSocketAddress());
				} catch (IOException e) 
				{
					//接受客户端连接异常？，具体是哪种异常，原因呢？
					e.printStackTrace();
					break ;
				}
											
			}//end_while(true)
			
    	}//end_run()
    }
    
    /*
     *  处理客户请求的线程
     *  主要功能是接受蓝牙控制命令，并且发送给机器人
     *  能及时地识别出来sockett被远程关闭
     */
    class BtFwClientThread extends Thread
    {
       	private Socket mcmsocket;        //与客户端通信的socket   
    	private DataOutputStream mdos;
    	private DataInputStream mdis;
    	private byte[] recvcmdbuf = new byte[CMDMAXLENTOROBOT];//接受客户端发送的命令信息
    	private int cmdlen;              //接受到服务器命令的长度	
    	private int msg_type;
    	public BtFwClientThread(Socket csc)
    	{
    		mcmsocket = csc;
    	}
    	
    	@Override
    	public void run()
    	{
			try 
			{
				mdos = new DataOutputStream(mcmsocket.getOutputStream());
				mdis = new DataInputStream(mcmsocket.getInputStream());
				NET_STATE = NET_STATE_CONNECTED;
			} catch (IOException e) 
			{
				//抛出异常？ 会是哪种原因呢？查！
				clean();
				e.printStackTrace();
				return ;
			}
			//注意，发送端不能发送的太频繁，否则CPU占用率太高，导致另一个httpserver太卡
			while(true)
			{
				try
				{
					msg_type = mdis.readInt();
					if(MSG_AUTOCTL_TRACE == msg_type)
					{
						cmdlen = mdis.readInt();

						if(cmdlen >= CMDMAXLENTOROBOT)
							cmdlen = CMDMAXLENTOROBOT;
						
						mdis.readFully(recvcmdbuf , 0 , cmdlen);
						
						if(1 == recvcmdbuf[0]) //开启人物跟踪
						{
							isCtrObjectTra = true;
							Log.i("cyy" , "start obj tracing");
						}
						else  //关闭人物跟踪
						{
							isCtrObjectTra = false;
							Log.i("cyy" , "stop obj tracing");
						}
					}
					else if(MSG_MANULCONTROL == msg_type)
					{
						cmdlen = mdis.readInt();

						if(cmdlen >= CMDMAXLENTOROBOT)
							cmdlen = CMDMAXLENTOROBOT;
						
						mdis.readFully(recvcmdbuf , 0 , cmdlen);
						Log.i("cyy" , "recive msg from client"+ new String(recvcmdbuf , 0 , cmdlen));
						sendCmd(recvcmdbuf , 0 , cmdlen );    //发送给远程蓝牙，不能更新UI，否则异常！
					}
                   					
					
					sleep(100);  //休眠100毫秒
					if(mcmsocket.isClosed())  //貌似反应很慢，应该开启另一个端连接？
						break;
					
				} catch (IOException e) 
				{
					// TODO Auto-generated catch block
					e.printStackTrace();
					break;
				} catch (InterruptedException e) 
				{
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
			clean();
	
    	}
    	
    	void write(byte[] buffer , int offset , int len)
    	{
    		try
    		{
				mdos.write(buffer, offset, len);
			} catch (IOException e) 
			{
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
    	}
    	
    	void writeInt(int val)
    	{
    		try
    		{
				mdos.writeInt(val);
			} catch (IOException e) 
			{
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
    	}
    	
    	void clean()
    	{
			try {
				NET_STATE = NET_STATE_NONE;
				mdos.close();
				mdis.close();
				mcmsocket.close();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}

    	}
    	
    }
    
    
    private void setNetState(int state)
    {
    	if(state!=NET_STATE_CONNECTED || state!= NET_STATE_LISTENNING || state!= NET_STATE_NONE)
    	{
    		return;
    	}
    	else
    	{
    		NET_STATE = state;
    	}
    	
    }
    
    public int getNetState()
    {
    	return NET_STATE;
    }
    


    
    //YUV420旋转90度
    static void YUVRotate90(byte[] des, byte[] src,int width,int height) 
    { 
        int i=0,j=0,n=0; 
        int hw=width/2,hh=height/2; 
        
        for(j=width;j>0;j--) 
        {
            for(i=0;i<height;i++) 
            {
               des[n++] = src[width*i+j]; 
            }
        }

         
         for(j=hw;j>0;j--)
         {
             for(i=0;i<hh;i++) 
             {
                 des[n++] = src[width*height + hw*i+j];
             }
         }
         
            
          for(j=hw;j>0;j--)
          {
              for(i=0;i<hh;i++)
              { 
            	  des[n++] = src[width*height*5/4 + hw*i +j];
              } 
          }
          
    }  
}
